﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace theRift
{
    public abstract class Camera
    {
        // Parameters
        protected Vector3 position; // Camera position
        protected Vector3 direction; // Where to look at
        protected Vector3 up; // Up vector

        protected Matrix view;
        protected Matrix projection;
        protected bool needViewUpdate;
        protected bool needProjectionUpdate;
        protected bool needFrustumUpdate;

        protected BoundingFrustum frustum;

        protected float fov;
        protected float aspectRatio;
        protected float nearPlane, farPlane;

        public Vector3 Position
        {
            get { return position; }
            set
            {
                position = value;
                needViewUpdate = true;
            }
        }

        public Vector3 Direction
        {
            get { return direction; }
            set 
            { 
                direction = value;
                needViewUpdate = true;
            }
        }

        public Vector3 Up
        {
            get { return up; }
            set 
            { 
                up = value;
                needViewUpdate = true;
            }
        }

        public float AspectRatio
        {
            get { return aspectRatio; }
            set 
            { 
                aspectRatio = value;
                needProjectionUpdate = true;
            }
        }

        public float FOV
        {
            get { return fov; }
            set 
            { 
                fov = value;
                needProjectionUpdate = true;
            }
        }

        public float NearPlane
        {
            get { return nearPlane; }
            set 
            { 
                nearPlane = value;
                needProjectionUpdate = true;
            }
        }

        public float FarPlane
        {
            get { return farPlane; }
            set 
            { 
                farPlane = value;
                needProjectionUpdate = true;
            }
        }

        public Matrix View
        {
            get
            {
                if (needViewUpdate)
                    UpdateView();

                return view;
            }
        }

        public Matrix Projection
        {
            get
            {
                if (needProjectionUpdate)
                    UpdateProjection();

                return projection;
            }
        }

        public BoundingFrustum Frustum
        {
            get
            {
                if (needProjectionUpdate)
                    UpdateProjection();

                if (needViewUpdate)
                    UpdateView();

                if (needFrustumUpdate)
                    UpdateFrustum();

                return frustum;
            }
        }

        public abstract void Update(GameTime gameTime);
      
        // Update view matrix
        protected virtual void UpdateView()
        {
            view = Matrix.CreateLookAt(position, 
                direction + position, up);

            needViewUpdate = false;
            needFrustumUpdate = true;
        }

        // Update projection matrix
        protected virtual void UpdateProjection()
        {
            projection = Matrix.CreatePerspectiveFieldOfView(fov, aspectRatio, 
                nearPlane, farPlane);

            needProjectionUpdate = false;
            needFrustumUpdate = true;
        }

        // Update bounding frustum
        protected virtual void UpdateFrustum()
        {
            frustum = new BoundingFrustum(view * projection);

            needFrustumUpdate = false;
        }
    }
}